"""
@author: 江同学呀
@file: privy_council.py
@date: 2023/8/22 19:45
@desc: ES军机处
"""


from typing import Type, List, Union, Literal, Dict, Set

from elasticsearch import Elasticsearch

from espc.logger.logger import logger, ERROR
from espc.core.core import Core, core
from espc.orm.model.model.base_index import BaseIndex


class ESPrivyCouncil:
    """
    ESPrivyCouncil

    初始化军机处对象时，必须传入配置信息，路径或字典或参数三选一。

    - pc_config_path: 军机处配置文件路径
    - pc_config: 字典类型，军机处配置信息

    - es_config_path: es配置文件路径
    - es_config_path: 字典类型，es配置信息

    - session_class: 会话类型，一个ES军机处实例只能选择一种会话：
        1. Session: 普通会话，每次都创建一个新的会话
        2. GlobalSession: 全局会话，全局只有一个会话
        3. ThreadSingleSession: 线程级会话，每个线程一个会话
    - logger_level: ES军机处的日志等级，默认debug级别

    - hosts: es服务器地址列表
    - sniffer_timeout: 启动前嗅探es集群服务器
    - sniff_on_start: es集群服务器结点连接异常时是否刷新es节点信息
    - sniff_on_connection_fail: 刷新节点信息时间间隔
    """
    _instance = None
    _core: Core = core

    def __init__(
            self,
            logger_level: Literal[
                "TRACE", "DEBUG", "INFOR", "CONFIRM", "QUITE", "ALERT", "ERROR", "MISINFORM", "NONES", "FAILS"
            ] = ERROR,
            *args, **kwargs
    ):
        logger.init_logger(level=logger_level)
        if args or kwargs:
            self.init_es(*args, **kwargs)
        return

    def __new__(cls, *args, **kwargs):
        if not isinstance(cls._instance, cls):
            cls._instance = super().__new__(cls)
        return cls._instance

    @property
    def es(self) -> Elasticsearch:
        return self._core.es

    def init_es(self, *args, **kwargs):
        """
        初始化ES
        :param hosts:
            Elasticsearch实例的主机列表，可以是字符串或字典。
            例如: `["http://localhost:9200"]` 或 `[{"host": "localhost", "port": 9200}]`。
        :param cloud_id:
            用于连接到Elastic Cloud的ID。
            例如: `"my-deployment:abc123def456ghi789jkl01234567890abcdef"`。
        :param api_key:
            用于身份验证的API密钥。
            例如: `("id", "api_key")`。
        :param basic_auth:
            用于基本身份验证的用户名和密码元组。
            例如: `("username", "password")`。
        :param bearer_auth:
            用于Bearer Token身份验证的令牌。
            例如: `"your_bearer_token"`。
        :param opaque_id:
            用于请求头的不透明ID，用于跟踪请求。
            例如: `"unique_request_id"`。
        :param headers:
            自定义HTTP请求头。
            例如: `{"User-Agent": "my-app/1.0"}`。
        :param connections_per_node:
            每个节点的最大连接数。
            例如: `10`。
        :param http_compress:
            是否启用HTTP压缩。
            例如: `True`。
        :param verify_certs:
            是否验证SSL证书。
            例如: `True`。
        :param ca_certs:
            CA证书文件路径。
            例如: `"/path/to/ca_certs.pem"`。
        :param client_cert:
            客户端证书文件路径。
            例如: `"/path/to/client_cert.pem"`。
        :param client_key:
            客户端密钥文件路径。
            例如: `"/path/to/client_key.pem"`。
        :param ssl_assert_hostname:
            是否验证主机名。
            例如: `True`。
        :param ssl_assert_fingerprint:
            用于验证SSL证书的指纹。
            例如: `"ab:cd:ef:12:34:56:78:90:12:34:56:78:90:12:34:56:78:90:12:34"`。
        :param ssl_version:
            使用的SSL版本。
            例如: `ssl.PROTOCOL_TLSv1_2`。
        :param ssl_context:
            自定义SSL上下文。
            例如: `ssl.create_default_context()`。
        :param ssl_show_warn:
            是否显示SSL警告。
            例如: `True`。
        :param transport_class:
            传输类。
            例如: `Transport`。
        :param request_timeout:
            请求超时时间（秒）。
            例如: `10`。
        :param node_class:
            节点类。
            例如: `BaseNode`。
        :param node_pool_class:
            节点池类。
            例如: `NodePool`。
        :param randomize_nodes_in_pool:
            是否随机化节点池中的节点。
            例如: `True`。
        :param node_selector_class:
            节点选择器类。
            例如: `NodeSelector`。
        :param dead_node_backoff_factor:
            死节点退避因子。
            例如: `2`。
        :param max_dead_node_backoff:
            最大死节点退避时间。
            例如: `60`。
        :param serializer:
            序列化器。
            例如: `Serializer`。
        :param serializers:
            序列化器字典。
            例如: `{"application/json": JsonSerializer()}`。
        :param default_mimetype:
            默认MIME类型。
            例如: `"application/json"`。
        :param max_retries:
            最大重试次数。
            例如: `3`。
        :param retry_on_status:
            在哪些HTTP状态码下重试。
            例如: `[502, 503, 504]`。
        :param retry_on_timeout:
            是否在超时时重试。
            例如: `True`。
        :param sniff_on_start:
            启动时是否嗅探节点。
            例如: `True`。
        :param sniff_before_requests:
            在每次请求前是否嗅探节点。
            例如: `True`。
        :param sniff_on_node_failure:
            在节点失败时是否嗅探节点。
            例如: `True`。
        :param sniff_timeout:
            嗅探超时时间（秒）。
            例如: `10`。
        :param min_delay_between_sniffing:
            两次嗅探之间的最小延迟时间（秒）。
            例如: `60`。
        :param sniffed_node_callback:
            嗅探节点后的回调函数。
            例如: `lambda nodes: print(nodes)`。
        :param meta_header:
            元数据请求头。
            例如: `True`。
        :param timeout:
            连接超时时间（秒）。
            例如: `10`。
        :param randomize_hosts:
            是否随机化主机列表。
            例如: `True`。
        :param host_info_callback:
            主机信息回调函数。
            例如: `lambda host: print(host)`。
        :param sniffer_timeout:
            嗅探超时时间（秒）。
            例如: `10`。
        :param sniff_on_connection_fail:
            在连接失败时是否嗅探节点。
            例如: `True`。
        :param http_auth:
            HTTP认证信息。
            例如: `("username", "password")`。
        :param maxsize:
            连接池的最大大小。
            例如: `10`。
        :param _transport:
            自定义传输对象。
            例如: `Transport()`。
        """
        self._core.init_es(*args, **kwargs)
        return

    def create_index(self, *args: Type[BaseIndex]):
        """
        创建索引
        :param args:
        :return:
        """
        exist_index_alias_dict: Dict = self._core.es.indices.get_alias().raw
        exist_index_alias_dict: Dict[str, Set[str]] = {
            index_name: set(alias_dict["aliases"].keys())
            for index_name, alias_dict in exist_index_alias_dict.items()
        }
        for index in args:
            if index.index_name in exist_index_alias_dict:
                continue
            self._core.es.indices.create(index=index.index_name, body=index._build())

            # 检查是否存在别名，并添加别名
            if index.alias:
                if isinstance(index.alias, List):
                    for alias in index.alias:
                        alias_name: str = alias if isinstance(alias, str) else alias.alias_name
                        if alias_name in exist_index_alias_dict.get(index.index_name, {}):
                            continue
                        self._core.es.indices.put_alias(index=index.index_name, name=alias_name)
                else:
                    alias_name: str = index.alias if isinstance(index.alias, str) else index.alias.alias_name
                    if alias_name in exist_index_alias_dict.get(index.index_name, {}):
                        continue
                    self._core.es.indices.put_alias(index=index.index_name, name=alias_name)
            pass
        return



